<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>renderElementFromSchema</title>
</head>
<style>
    * {
        padding: 0;
        margin: 0;
    }

    #page {
        display: flex;
        height: 100vh;
        width: 100vw;
    }

    #page .left {
        width: 50%;
        border-right: 1px solid;
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
    }

    #page .left #schemaInput {
        margin-bottom: 20px;
        width: 50%;
        height: 40%;
    }

    #page .left button {
        width: 20%;
        height: 10%;
        cursor: pointer;
    }

    #page .right {
        width: 50%;
        display: flex;
        justify-content: center;
        align-items: center;
    }

    #page .right form {
        display: flex;
        flex-direction: column;
        width: 30%;
    }
</style>

<body>
    <div id="page">
        <div class="left">
            <textarea name="schemaInput" id="schemaInput" placeholder="请输入Schema..."></textarea>
            <button id="parseSchema">解析Schema</button>
        </div>
        <div class="right">
            <form id="view" novalidate>

            </form>
        </div>
    </div>
</body>
<script>
    // 测试数据
    let schemaJSON = [
        {
            "name": "Username1",
            "inputType": "text",
            "label": "用户名",
            "rules": [{
                // "maxlength": 10,
                // "minlength": 3,
                "required": true,
                "pattern": {
                    "reg": "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}",
                    "type": ['blur'],
                    "message": "请输入正确邮箱地址"
                },

            }],
            "events": [
                {
                    "onkeydown": "{{console.log('Username1.text:'+Username1.text)}}"
                },
                {
                    "onclick": "{{console.log('点击了用户名输入框')}}"
                }
            ],
            "props": [{
                "class": 'testClass',
                "style": "height:50px",
            }]
        },
        {
            "name": "Test",
            "inputType": "number",
            "label": "测试",
            "rules": [{ "max": 4 }],
            "events": [
                {
                    "onkeydown": "{{console.log('Test.text:'+Test.text)}}"
                },
                {
                    "onclick": "{{console.log('点击了测试输入框')}}"
                }
            ]
        },
        {
            "name": "Password1",
            "inputType": "password",
            "label": "密码",
            "rules": [{ "maxlength": 10, "required": true, "max": 4 }],
            "events": [
                {
                    "onkeydown": "{{console.log('Password1.text:'+Password1.text)}}"
                },
                {
                    "onclick": "{{console.log('点击了密码输入框')}}"
                }
            ]
        },
        {
            "name": "Button1",
            "inputType": "button",
            "text": "提交",
            "events": [
                {
                    "onclick": "{{console.log('Username1.text:',Username1.text,'Password1.text:'+Password1.text)}}"
                }
            ]
        }
    ]
    let textarea = document.getElementById('schemaInput')
    textarea.value = JSON.stringify(schemaJSON)
    // 创建全局校验失败消息列表
    window['errorMessage'] = []
    // 解析 Schema 并创建元素
    function createElementFromSchema(schema) {

        // 如果 schema 是数组，递归处理每个元素
        if (Array.isArray(schema)) {
            return schema.map(createElementFromSchema);
        }

        // 创建全局变量
        window[schema['name']] = {}

        // 创建input元素
        const element = document.createElement('input');

        // 设置input类型
        element.setAttribute('type', schema['inputType'])

        // 设置统一属性
        if (schema['props']) {
            schema['props'].forEach(item => {
                for (key in item) {
                    element.setAttribute(key, item[key]);
                }
            })
        }

        if (schema['inputType'] != 'button') {

            // 创建label元素
            const label = document.createElement('label');
            label.setAttribute('for', schema['name'])
            label['textContent'] = schema['label'] + ':'
            // 设置label
            element.setAttribute('name', schema['name'])
            element.setAttribute('id', schema['name'])


            // 绑定事件
            schema['events'].forEach(item => {
                for (key in item) {
                    const eventType = key.slice(2).toLowerCase();
                    // 组装keydown事件
                    // 1.给全局变量赋值
                    // 2.执行schema传过来的代码块
                    const handlerFunction = new Function('e', 'copyWindow', `
                        const currentValue = e.target.value;
                        const key = e.key;
                        //获取最新value值
                        const cursorPosition = e.target.selectionStart;
                        const newValue = currentValue.slice(0, cursorPosition) + key + currentValue.slice(cursorPosition);
                        // 给全局变量赋值 通过浅拷贝修改相同引用地址
                        copyWindow['text'] = newValue;`+ item[key].slice(2, -2));
                    element.addEventListener(eventType, (event) => handlerFunction(event, window[schema['name']]));
                }
            })

            // 处理校验规则
            if (schema['rules']) {
                schema['rules'].forEach(item => {
                    for (key in item) {
                        // 必填时添加‘*’提示
                        if (key == 'required') {
                            if (item[key]) {
                                const required = document.createElement('span');
                                required.textContent = '*'
                                required.setAttribute('style', 'color:red');
                                label.appendChild(required)
                                element.setAttribute(key, item[key]);
                            }
                        } else if (key == 'pattern') {
                            // 正则类型处理
                            item[key]['type'].forEach(i => {
                                element.addEventListener(i, (e, key) => {
                                    if (!new RegExp(item['pattern']['reg']).test(window[schema['name']]['text'])) {
                                        if (!window['errorMessage'].includes(item['pattern']['message'])) {
                                            window['errorMessage'].push(item['pattern']['message'])
                                        }
                                    } else {
                                        let index = window['errorMessage'].findIndex(m => m == item['pattern']['message'])
                                        window['errorMessage'].splice(index, 1)
                                    }
                                })
                            })
                        } else {
                            element.setAttribute(key, item[key]);
                        }

                    }
                })
            }

            return [label, element];
        } else {
            // 设置button文字
            element.setAttribute('value', schema['text'])
            element.setAttribute('type', 'submit')

            // 绑定事件
            schema['events'].forEach(item => {
                for (key in item) {
                    // 组装keydown事件
                    // 1.执行schema传过来的代码块
                    // 2.校验信息提示
                    element[key] = new Function('e', `
                        window['errorMessage'].forEach(i => {
                            alert(i)
                        });
                    `+ item[key].slice(2, -2));
                }
            })

            return [element];
        }
    }
    // 渲染 DOM
    function renderSchema(schema, container) {
        const elements = createElementFromSchema(schema);
        const flatEleList = elements.flat(Infinity)
        flatEleList.forEach(element => container.appendChild(element));
    }

    const view = document.getElementById('view');
    // 解析按钮添加事件
    const btn = document.getElementById('parseSchema')
    btn.addEventListener('click', () => {
        // 第二次解析时删除上次解析的form所有子元素
        let viewChild = [...view.children]
        if (viewChild) {
            viewChild.forEach(item => {
                view.removeChild(item)
            })
        }
        renderSchema(JSON.parse(textarea.value), view)
    })
    view.addEventListener('submit', function (event) {
        if (!view.checkValidity()) {
            // 如果表单无效，浏览器会显示默认的验证信息
            view.reportValidity();
        }
        event.preventDefault(); // 阻止表单的默认提交行为
    });
</script>

</html>